﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IMLibrary.ExZeroMQ;
using IMLibrary.BinaryPacket; 
using P;
using Ourmsg.Enum;
using Ourmsg.Factory;
using Ourmsg.Server.MemoryObject;

namespace Ourmsg.Server
{
    /// <summary>
    /// 消息路由服务器
    /// </summary>
    public class RouterServer
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="MqPort">消息队列服务端口</param>
        public RouterServer(int MqPort)
        {
            Port = MqPort;
        }

        #region 字段
        /// <summary>
        /// 路由器中所有用户在线状态信息,采用数组(指针)是增加访问速度
        /// </summary>
        RouterUser[] Users = new  RouterUser[10000000]; 
        /// <summary>
        /// 消息队列服务器
        /// </summary>
        MQServer MqServer;
        /// <summary>
        /// 离线消息存取客户端
        /// </summary>
        OfflineMessageClient offlineMsgClient;
        #endregion

        #region 属性

        public int Port
        { set; get; }

        #endregion
         
        #region 私有方法 
        private void MqServer_Receive(object sender, MQEventArgs e)
        {
            if (e.Data.Length < BinaryMsg.HeaderLength) return; //非法消息退出处理
            BinaryMsg msg = new BinaryMsg(e.Data);
            switch (msg.CommandType)
            {
                #region 用户登录与在线状态更改
                case (byte)CommandType.UserLogined: 
                case (byte)CommandType.Presence:
                    OnUserStatus(e.ClientID, msg);
                    break;
                #endregion

                #region 用户消息
                case (byte)CommandType.UserMessage:
                    SendMsgToUser(msg.From, msg.To, CommandType.UserMessage, msg.Payload, false, true);
                    break;
                #endregion

                #region 群消息
                case (byte)CommandType.RoomMessage:
                    SendMsgToRoom(msg.From, msg.To, CommandType.RoomMessage, msg.Payload, true);
                    break;
                #endregion  

                #region 处理用户请求添加好友
                case (byte)CommandType.RequestAddFriend:
                    {
                        var From = msg.From;
                        var To = msg.To;
                        var invMsg = XML.ConvertToObject(msg.GetPayloadString()) as InviteMsg;
                        if (invMsg == null) return;
                        var user = Cache.GetUser(To);
                        if (user == null) return;//如果要添加的好友不存在，则退出
                        if (user.AddCondition == AddCondition.AllowAll)//如果允许任何人添加为好友
                        {
                            Cache.AddFriend(From, To);//在缓存中将两个用户添加为好友关系
                            DBO.AddFriend(From, To, invMsg.GroupID, invMsg.RemarkName, -1, "");//在数据库中将两个用户添加为好友关系

                            var sysMsg = new P.Message();
                            sysMsg.SysMsg = true;
                            sysMsg.Content = "你们已经成为好友，现在可以聊天了！";

                            //发送添加成功的好友信息给用户
                            var userBaseInfos = DBO.GetUserAndFriendBaseInfos(From, To);
                            if (userBaseInfos != null && userBaseInfos.Count == 2)
                            {
                                foreach (var userBase in userBaseInfos)
                                    if (userBase.UserID == From)
                                    {
                                        SendMsgToUser(From, To, CommandType.FriendBaseData, XML.ConvertToArray(userBase));
                                        SendMsgToUser(From, To, CommandType.AddFriendSuccess, XML.ConvertToArray(sysMsg));
                                    }
                                    else
                                    {
                                        SendMsgToUser(To, From, CommandType.FriendBaseData, XML.ConvertToArray(userBase));
                                        SendMsgToUser(To, From, CommandType.AddFriendSuccess, XML.ConvertToArray(sysMsg));
                                    }
                            }
                        }
                        else if (user.AddCondition == AddCondition.NeedAuditing)//如果允许通过认证添加为好友
                        {
                            Cache.AddFriendProof(From, To, invMsg.GroupID, invMsg.RemarkName);//添加凭据到缓存中
                            invMsg.GroupID = -1;
                            invMsg.RemarkName = ""; 
                            SendMsgToUser(From, To, CommandType.RequestAddFriend, XML.ConvertToArray(invMsg));
                        }
                    }
                    break;
                #endregion

                #region 回应请求添加好友
                case (byte)CommandType.ResponsesAddFriend:
                    {
                        var From = msg.From;
                        var To = msg.To;
                        var invMsg = XML.ConvertToObject(msg.GetPayloadString()) as InviteMsg;
                        if (invMsg == null) return;
                        if (invMsg.Result != InviteResult.Yes) return;//如果拒绝添加则退出
                        var proof = Cache.GetAddFriendProof(To, From);

                        if (proof == null) return;
                        {
                            Cache.AddFriend(To, From);//在缓存中将两个用户添加为好友关系
                            DBO.AddFriend(From, To, invMsg.GroupID, invMsg.RemarkName, -1, "");//在数据库中将两个用户添加为好友关系

                            var sysMsg = new P.Message();
                            sysMsg.SysMsg = true;
                            sysMsg.Content = "你们已经成为好友，现在可以聊天了！";

                            //发送添加成功的好友信息给用户
                            var userBaseInfos = DBO.GetUserAndFriendBaseInfos(From, To);
                            if (userBaseInfos != null && userBaseInfos.Count == 2)
                            {
                                foreach (var userBase in userBaseInfos)
                                    if (userBase.UserID == From)
                                    {
                                        SendMsgToUser(From, To, CommandType.FriendBaseData, XML.ConvertToArray(userBase));
                                        SendMsgToUser(From, To, CommandType.AddFriendSuccess, XML.ConvertToArray(sysMsg));
                                    }
                                    else
                                    {
                                        SendMsgToUser(To, From, CommandType.FriendBaseData, XML.ConvertToArray(userBase));
                                        SendMsgToUser(To, From, CommandType.AddFriendSuccess, XML.ConvertToArray(sysMsg));
                                    }
                            }
                        } 
                    }
                    break;
                #endregion

                #region 请求删除好友
                case (byte)CommandType.DelFriend:
                    {
                        Cache.DelFriend(msg.From, msg.To);
                        var t = DBO.DelFriend(msg.From, msg.To);
                        if (t)
                        {
                            SendMsgToUser(msg.To, msg.From, CommandType.DelFriend,null);
                            SendMsgToUser(msg.From, msg.To, CommandType.DelFriend, null); 
                        }
                    }
                    break;
                #endregion

                #region  处理创建群操作
                case (byte)CommandType.CreateRoom:
                    {
                        var r = XML.ConvertToObject(msg.GetPayloadString()) as RoomBaseInfo;
                        if (r == null) return;//如果群对像为空，则退出

                        if (r.RoomName == null || r.RoomName.Trim() == string.Empty)
                            return;//无群名视为非法创建,则退出

                        var From = msg.From;
                        r.CreateUserID = From;
                        var roomID = CacheCreateRoom.GetUnusedID();//从缓存中获取一个未被使用的群号

                        if (roomID == 0) return;

                        r.RoomID = roomID;
                        List<UInt32> invUsers = null;
                        if (r.InviteUsers != null && r.InviteUsers != string.Empty)
                        {
                            string[] Ids = r.InviteUsers.Split(',');
                            r.InviteUsers = null;
                            if (Ids != null)
                            {
                                invUsers = new List<uint>();
                                foreach (var id in Ids)
                                    if (id != string.Empty)
                                        try
                                        {
                                            invUsers.Add(UInt32.Parse(id));
                                        }
                                        catch { }
                            }
                        }

                        r.MaxUsersCount = 500;
                        r.GroupID = -1;

                        DBO.CreateRoom(r, invUsers);//在数据库中创建并保存群

                        AddCondition addCondition = AddCondition.AllowAll;
                        if (r.AddCondition == (byte)AddCondition.NeedAuditing)
                            addCondition = AddCondition.NeedAuditing;
                        else if (r.AddCondition == (byte)AddCondition.RefuseALL)
                            addCondition = AddCondition.RefuseALL;
                        var cacheRoom = new RouterCacheRoom()//创建群缓存对像
                        {
                            RoomID = roomID,
                            RoomName = r.RoomName,
                            CreateUserID = r.CreateUserID,
                            AddCondition = addCondition,
                            MaxUsersCount = r.MaxUsersCount
                        };
                        Cache.CreateRoom(cacheRoom, invUsers, roomID);//在缓存中保存群信息

                        SendMsgToUser(roomID, From, CommandType.CreateRoom, XML.ConvertToArray(r));//通知用户群创建成功
                        SendMsgToRoom(From, roomID, CommandType.RoomBaseData, XML.ConvertToArray(r));//发送新群创建成功消息给所有群用户
                    }
                    break; 
                #endregion

                #region 处理用户请求添加群
                case (byte)CommandType.RequestAddRoom:
                    {
                        var From = msg.From;
                        var RoomID = msg.To;
                        var invMsg = XML.ConvertToObject(msg.GetPayloadString()) as InviteMsg;
                        if (invMsg == null) return;
                        RouterCacheRoom room = Cache.GetRoom(RoomID);
                        if (room == null) return;
                        invMsg.RoomName = room.RoomName;
                      
                        if (room.AddCondition == (byte)AddCondition.AllowAll)
                        {
                            RouterCacheRoomUser ru = new RouterCacheRoomUser();
                            ru.UserID = From;
                            Cache.AddUserToRoom(RoomID,ru );

                            #region 创建群基本信息XML
                            RoomBaseInfo rinfo = new RoomBaseInfo();
                            rinfo.RoomID = room.RoomID;
                            rinfo.RoomName = room.RoomName;
                            rinfo.CreateUserID = room.CreateUserID;
                            rinfo.MaxUsersCount = room.MaxUsersCount; 
                            rinfo.GroupID = invMsg.GroupID;
                            rinfo.RemarkName = invMsg.RemarkName;
                            ///将群基本信息发送给成功加入群的当前用户
                            SendMsgToUser(From, From, CommandType.RoomBaseData, XML.ConvertToArray(rinfo));
                            #endregion

                            #region 发送成功添加群系统信息给用户
                            P.Message sysMsg = new P.Message();//发送一条消息给对方.
                            sysMsg.SysMsg = true;
                            sysMsg.Content = "您已经加入群，可以和群成员聊天了！";
                            SendMsgToUser(From, From, CommandType.RoomMessage, XML.ConvertToArray(sysMsg));
                            #endregion

                            #region 通知群管理员用户加入群
                            //sysMsg.Content = string.Format("{0}({1})已经加入群！", invMsg.UserName, invMsg.UserID);
                            //var palyload = XML.ConvertToArray(sysMsg);
                            //var roomAdmins = Cache.GetRoomAdmins(RoomID);
                            //foreach (var u in roomAdmins)
                            //    SendMsgToUser(From, u.UserID, CommandType.RoomMessage, palyload);//发送加入群信息给群管理员 
                            #endregion
                        }
                        else if (room.AddCondition ==  AddCondition.NeedAuditing)
                        {
                            Cache.AddRequestAddRoomProof(RoomID, From, invMsg.GroupID, invMsg.RemarkName);//添加凭据到缓存中
                            var roomAdmins = Cache.GetRoomAdmins(RoomID);//获取群所有管理员
                            foreach (var u in roomAdmins)//发送加入群信息给群管理员
                                SendMsgToUser(From, u.Value.UserID, CommandType.RequestAddRoom, XML.ConvertToArray(invMsg));
                        }
                    }
                    break;
                #endregion
                    
                #region 处理用户请求添加群管理员的回应
                case (byte)CommandType.ResponsesAddRoom:
                    {
                        var From = msg.From;
                        var RoomID = msg.To;
                        var invMsg = XML.ConvertToObject(msg.GetPayloadString()) as InviteMsg;
                        if (invMsg == null) return;
                        if (invMsg.Result != InviteResult.Yes) return;//如果拒绝添加则退出
                        var room = Cache.GetRoom(RoomID);
                        if (room == null) return;
                        invMsg.RoomName = room.RoomName;
                        var userID = invMsg.UserID;

                        RouterCacheRoomUser u = Cache.GetRoomUser(RoomID, From);
                        if (u == null || !u.Admin) return;//如果用户不存在或用户不是管理员，则退出操作
                        var proof = Cache.GetRequestAddRoomProof(RoomID, userID);
                        if (proof == null) return;//如果不存在凭据
                        { 
                            Cache.AddUserToRoom(RoomID, new RouterCacheRoomUser() { UserID = userID });//添加用户到缓存群
                            DBO.AddUserToRoom(RoomID, userID, proof.GroupID,false, proof.RemarkName);//添加用户到数据库群中

                            #region 创建群基本信息XML
                            RoomBaseInfo rinfo = new RoomBaseInfo();
                            rinfo.RoomID = room.RoomID;
                            rinfo.RoomName = room.RoomName;
                            rinfo.CreateUserID = room.CreateUserID;
                            rinfo.MaxUsersCount = room.MaxUsersCount;
                            rinfo.RemarkName = proof.RemarkName;
                            rinfo.GroupID = proof.GroupID;
                            ///将群基本信息发送给成功加入群的当前用户
                            SendMsgToUser(From, userID, CommandType.RoomBaseData, XML.ConvertToArray(rinfo));
                            #endregion

                            #region 发送成功添加群系统信息给用户
                            var sysMsg = new P.Message();//发送一条消息给对方.
                            sysMsg.SysMsg = true;
                            sysMsg.Content = "您已经加入群，可以和群成员聊天了！";
                            SendMsgToUser(From, userID, CommandType.RoomMessage, XML.ConvertToArray(sysMsg));
                            #endregion
                        }
                    }
                    break;
                #endregion

                #region 处理用户回复邀请添加群
                case (byte)CommandType.ResponsesInviteAddRoom:
                    {

                    }
                    break;
                #endregion

                #region 处理管理员邀请添加群
                case (byte)CommandType.InviteAddRoom:
                    {
                        var From = msg.From;
                        var roomID = msg.To;
                        var invMsg = XML.ConvertToObject(msg.GetPayloadString()) as InviteMsg;
                        if (invMsg == null) return;
                        if (invMsg.AttachMsg == null || invMsg.AttachMsg == string.Empty) return;

                        //var admins = Cache.GetRoomAdmins(msg.To);//获得群管理员集合 
                        //if (!admins.Exists(user => user.UserID == msg.From && user.Admin ? true : false))
                        //    return;//如果操作用户不是管理员，则退出

                        var roomUser = Cache.GetRoomUser(roomID, From);
;                        if (roomUser == null || !roomUser.Admin) return;//如果操作用户不是管理员，则退出

                        List<UInt32> invUsers = null;

                        string[] Ids = invMsg.AttachMsg.Split(',');
                        invMsg.AttachMsg = null;
                        if (Ids != null)
                        {
                            invUsers = new List<uint>();
                            foreach (var id in Ids)
                                if (id != string.Empty)
                                    try
                                    {
                                        invUsers.Add(UInt32.Parse(id));
                                    }
                                    catch { }
                        }
                        if (invUsers == null || invUsers.Count == 0) return;//如果没有邀请任何用户，则退出

                        var cacheRoom = Cache.GetRoom(roomID);
                        if (cacheRoom == null) return;

                        Cache.AddUsersToRoom(roomID, invUsers);//在缓存中保存群信息
                        DBO.AddUsersToRoom(roomID, invUsers);//在数据库中添加邀请用户

                        #region 创建群基本资料,以便发送给邀请加入群的用户
                        RoomBaseInfo rBase = new RoomBaseInfo()
                        {
                            AddCondition = (byte)cacheRoom.AddCondition,
                            CreateUserID = cacheRoom.CreateUserID,
                            RoomName = cacheRoom.RoomName,
                            MaxUsersCount = cacheRoom.MaxUsersCount,
                            GroupID = -1,
                            RoomID = roomID,
                        };
                        var playload = XML.ConvertToArray(rBase);
                        #endregion

                        var c = new P.Collection();
                        c.Data = new List<object>();
                        foreach (var userID in invUsers)
                        {
                            var cUser = Cache.GetUser(userID);
                            if (cUser == null)
                                cUser = Cache.GetSysUser(userID);

                            if (cUser != null)
                            {
                                ShowType showType = ShowType.Offline;
                                if (Users[userID] != null)
                                    if (Users[userID].PC_ShowType != ShowType.Offline)
                                        showType = Users[userID].PC_ShowType;
                                    else
                                        showType = Users[userID].APP_ShowType;
                                var ru = new RoomUser()
                                { RoomID = roomID, HeadID = cUser.HeadID, ShowType = showType, UserID = userID, UserName = cUser.UserName };
                                c.Data.Add(ru);
                                SendMsgToUser(0, userID, CommandType.RoomBaseData, playload);//发送新群创建成功消息给所有群用户
                            } 
                        }
                        if (c.Data.Count > 0)
                            SendMsgToUser(0, From, CommandType.GetRoomUserData, XML.ConvertToArray(c));//发送邀请成功的用户资料给管理员
                    }
                    break;
                #endregion

                #region 设置群管理员
                case (byte)CommandType.SetRoomManger:
                    { 
                        var roomID = msg.To;
                        var From = msg.From; 
                        var roomUser = Cache.GetRoomUser(roomID, From);
                        if (roomUser == null || !roomUser.Admin) return;//如果操作用户不是管理员，则退出

                        var manger = XML.ConvertToObject(msg.GetPayloadString()) as RoomUser;
                        if (manger == null) return;//如果设置用户为空,则退出设置

                        if (manger.RoomID != msg.To) return;//如果是非法消息，则退出
                         
                        if (Cache.SetRoomAdmin(msg.To, manger.UserID, manger.Admin))
                        {
                            DBO.SetRoomAdmin(msg.To, manger.UserID, manger.Admin);
                            //通知用户设置成功 
                            SendMsgToUser(0, msg.From, CommandType.SetRoomManger, XML.ConvertToArray(manger));
                        }
                        Console.WriteLine("设置成功!");
                    }
                    break;
                #endregion

                #region 设置群成员名片
                case (byte)CommandType.SetRoomUserCard:
                    {
                        var roomID = msg.To;
                        var From = msg.From;
                        var roomUser = Cache.GetRoomUser(roomID, From);
                        if (roomUser == null || !roomUser.Admin) return;//如果操作用户不是管理员，则退出

                        RoomUser ru = XML.ConvertToObject(msg.GetPayloadString()) as RoomUser;
                        if (ru.RoomID != msg.To) return;//如果是非法消息，则退出 

                        if (ru.UserCard == null)
                            ru.UserCard = "";

                        DBO.SetUserVcard(msg.To, ru.UserID, ru.UserCard);
                        //通知用户设置成功 
                        SendMsgToUser(0, msg.From, CommandType.SetRoomUserCard, XML.ConvertToArray(ru));
                    }
                    break;
                #endregion

                #region 删除群成员
                case (byte)CommandType.DelRoomUser:
                    {
                        var roomID = msg.To;
                        var From = msg.From;
                        var roomUser = Cache.GetRoomUser(roomID, From);
                        if (roomUser == null || !roomUser.Admin) return;//如果操作用户不是管理员，则退出

                        RoomUser ru = XML.ConvertToObject(msg.GetPayloadString()) as RoomUser;
                        if (ru.RoomID != msg.To) return;//如果是非法消息，则退出  
                        Cache.DelUserFromRoom(ru.RoomID, ru.UserID);
                        DBO.DelUserFromRoom(ru.RoomID, ru.UserID);

                        var data = XML.ConvertToArray(ru);
                        SendMsgToRoom(0, ru.RoomID, CommandType.DelRoomUser, data); //通知所有群成员，用户已经删除
                        SendMsgToUser(From, ru.UserID, CommandType.DelRoomUser, data);//通知被删除的用户已经退出群
                    }
                    break;
                #endregion

                #region 用户退出群
                case (byte)CommandType.QuitRoom:
                    {
                        var roomID = msg.To;
                        var From = msg.From;

                        var room = Cache.GetRoom(roomID);
                        if (room != null && room.CreateUserID == From) return;//如果是群创建者，则解散群

                        {
                            var roomUser = Cache.GetRoomUser(roomID, From);
                            if (roomUser == null) return;//如果用户不存在，则退出
                        }

                        Cache.DelUserFromRoom(roomID, From);
                        var t = DBO.DelUserFromRoom(roomID, From);
                        if (t)
                        {
                            var user = new RoomUser() { RoomID = roomID, UserID = From };
                            var data = XML.ConvertToArray(user);
                            SendMsgToRoom(From, roomID, CommandType.QuitRoom, data); //通知所有群成员，用户已经删除
                            SendMsgToUser(From, From, CommandType.QuitRoom, data);//通知被删除的用户已经退出群
                        }
                    }
                    break;
                #endregion

                #region 设置群基本信息
                case (byte)CommandType.SetRoomBaseInfo:
                    {
                        var roomID = msg.To;
                        var From = msg.From;
                        var roomUser = Cache.GetRoomUser(roomID, From);
                        if (roomUser == null || !roomUser.Admin) return;//如果操作用户不是管理员，则退出

                        var rb = XML.ConvertToObject(msg.GetPayloadString()) as RoomBaseInfo;
                        if (rb.RoomID != msg.To) return;//如果是非法消息，则退出 

                        var addCondition = rb.AddCondition;
                        if (addCondition > 2) addCondition = 0;

                        DBO.SetRoomBaseInfo(rb.RoomID, rb.RoomName, addCondition, rb.Notice, rb.Remark);
                        //通知用户设置成功 
                        SendMsgToUser(0, msg.From, CommandType.SetRoomBaseInfo, XML.ConvertToArray(rb));
                    }
                    break;
                    #endregion
            }
        }
        #endregion

        #region 保存离线消息  
        /// <summary>
        /// 保存离线消息 
        /// </summary>
        /// <param name="userID"></param>
        /// <param name="msg"></param>
        private void SaveOfflineMsg(UInt32 userID, BinaryMsg  msg)
        {
            offlineMsgClient.SaveOfflineMsg(userID, msg.ToArray());
        }
        #endregion

        #region 处理用户在线状态
        /// <summary>
        /// 处理用户在线状态
        /// </summary>
        /// <param name="clientID"></param>
        /// <param name="msg"></param>
        private void OnUserStatus(byte[] clientID, BinaryMsg msg)
        {
            var obj = XML.ConvertToObject(msg.GetPayloadString());
            if (obj == null) return; 
            if (obj is Status)
            {
                var status = obj as Status;
                var cacheUser = new RouterCacheUser()
                { UserID = status.UserID, UserName = status.UserName, HeadID = status.HeadID, AddCondition = status.AddCondition };
                if (status.SysUser)
                    Cache.AddSysUser(cacheUser);
                else
                    Cache.AddUser(cacheUser);
            } 
            var p = obj as Presence;
            if (p == null) return;

            var userID = p.UserID;
            if (userID > Users.Length - 1) return;

            bool sysUser = p.SysUser;//标识此用户是否系统用户

            if (Users[userID] == null)
            {
                Users[userID] = new RouterUser();
                Users[userID].UserID = userID;
                Users[userID].APP_ShowType = ShowType.Offline;
                Users[userID].PC_ShowType = ShowType.Offline;
            }
             
            if (msg.APPClient)
            {
                Users[userID].APP_ShowType = p.ShowType;
                Users[userID].APP_ServerId = clientID;
            }
            else
            {
                Users[userID].PC_ShowType = p.ShowType;
                Users[userID].PC_ServerId = clientID;
            }  

            var user = Users[userID];
            if (user.PC_ShowType == ShowType.Offline)
                p.ShowType = user.APP_ShowType; 
            p.SysUser = false;
            var PresenceBytes = XML.ConvertToArray(p);

            if (sysUser)//如果用户是系统，则通知所有系统用户
            {
                var sysUsers = Cache.GetSystemUsers();
                foreach (var u in sysUsers)
                    SendMsgToUser(userID, u.Value.UserID, CommandType.FriendPresences, PresenceBytes);
                var unSysUsers = Cache.GetUnSystemUsers();
                foreach (var u in unSysUsers)
                    SendMsgToUser(userID, u.Value.UserID, CommandType.FriendPresences, PresenceBytes);
            }
            else//如果是公众用户，则通知好友其上线
            {
                #region 通知用户好友上线状态改变
                var friends = Cache.GetFriends(p.UserID);//获取用户好友
                foreach (var f in friends)
                    SendMsgToUser(userID, f.Value.FriendID, CommandType.FriendPresences, PresenceBytes);
                #endregion

                #region 通知用户所在群上线状态改变 
                var rooms = Cache.GetUserRooms(p.UserID);//获取用户群帐号
                foreach (var r in rooms)
                {
                    var rus = new PresenceRoom();
                    rus.RoomID = r.Value;
                    rus.UserID = userID;
                    rus.ShowType = p.ShowType;
                    SendMsgToRoom(userID, r.Value, CommandType.RoomFriendPresences, XML.ConvertToArray(rus));
                }
                #endregion
            } 
        }
        #endregion

        #region 发送消息
        /// <summary>
        /// 发送消息给用户
        /// </summary>
        /// <param name="From">消息发送者</param>
        /// <param name="To">消息接收者</param>
        /// <param name="CmdType">消息类型</param>
        /// <param name="Payload">消息内容</param>
        /// <param name="ToRoom">是否群消息</param>
        /// <param name="IsSave">是否保存离线消息</param>
        private void SendMsgToUser(UInt32 From, UInt32 To, CommandType CmdType, byte[] Payload, bool ToRoom, bool IsSave = false)
        {
            if (To > Users.Length - 1) return;

            BinaryMsg msg = new BinaryMsg((byte)CmdType);
            msg.From = From;
            msg.To = To;
            msg.IsSave = IsSave;
            msg.ToRoom = ToRoom;
            msg.Payload = Payload;

            var user = Users[To];
            if (user == null)//如果用户从未上过线
            {
                if (IsSave)
                    SaveOfflineMsg(To, msg);//存为离线消息
                return;
            } 
            if (user.PC_ShowType == ShowType.Offline && IsSave)
            {
                SaveOfflineMsg(To, msg);//存为离线消息 
            }

            if (user.PC_ShowType != ShowType.Offline)//如果用户PC客户端上线
                MqServer.Send(user.PC_ServerId, msg.ToArray());//发送消息到PC客户端
            if (user.APP_ShowType != ShowType.Offline)//如果用户APP客户端上线
                MqServer.Send(user.APP_ServerId, msg.ToArray());//发送消息到App客户端
        }

        /// <summary>
        /// 发送消息给用户
        /// </summary>
        /// <param name="From">消息发送者</param>
        /// <param name="To">消息接收者</param>
        /// <param name="CmdType">消息类型</param>
        /// <param name="Payload">消息内容</param>
        private void SendMsgToUser(UInt32 From, UInt32 To, CommandType CmdType, byte[] Payload)
        {
            SendMsgToUser(From, To, CmdType, Payload, false);
        }

        /// <summary>
        /// 发送消息到群
        /// </summary>
        /// <param name="From">消息发送者</param>
        /// <param name="RoomID">消息接收群</param>
        /// <param name="CmdType">消息类型</param>
        /// <param name="Payload">消息内容</param> 
        /// <param name="IsSave">是否保存离线消息</param>
        private void SendMsgToRoom(UInt32 From, UInt32 RoomID, CommandType CmdType, byte[] Payload, bool IsSave = false)
        {
            if (From != 0)//如果不是系统发送消息
            {
                var user = Cache.GetRoomUser(RoomID, From);
                if (user == null || user.Ban)
                    return;//如果用户被禁止发言，则退出
            }

            var roomUsers = Cache.GetRoomUsers(RoomID);
            foreach (var u in roomUsers)
                if (From != u.Value.UserID)//如果用户不是消息发送者!
                    SendMsgToUser(From, u.Value.UserID, CmdType, Payload, true, IsSave);
        }

        /// <summary>
        /// 发送消息到群
        /// </summary>
        /// <param name="From">消息发送者</param>
        /// <param name="RoomID">消息接收群</param>
        /// <param name="CmdType">消息类型</param>
        /// <param name="Payload">消息内容</param>
        private void SendMsgToRoom(UInt32 From, UInt32 RoomID, CommandType CmdType, byte[] Payload)
        {
            SendMsgToRoom(From, RoomID, CmdType, Payload, false);
        }
        #endregion
         
        #region 公共方法
        /// <summary>
        /// 启动服务
        /// </summary>
        public void Start()
        {
            DBO.SetDBAllUsersOffline();//将数据库中所有用户置于离线 
            Cache.OpenNoSQL();//打开本地缓存数据库  
            Cache.LoadDBtoCache();//将数据库中用户和群的基础数据加载到缓存中,以实现高速访问与存储
            if (MqServer == null)
            {
                MqServer = new MQServer(Port, ServerType.Router);
                MqServer.Receive += MqServer_Receive;
                MqServer.StartAsyncReceive();
            } 
            if(offlineMsgClient ==null )
            {
                offlineMsgClient = new OfflineMessageClient();
                offlineMsgClient.OpenConnection();//连接离线存取消息服务器
            }
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            if (MqServer != null)
            {
                MqServer.Receive -= MqServer_Receive;
                MqServer.Dispose();
                MqServer = null;
            }
            if (offlineMsgClient != null)
            {
                offlineMsgClient.CloseConnection();
                offlineMsgClient = null;
            }

            ///将内存中用户设置为离线
            for (int i = 0; i < Users.LongLength; i++)
                if (Users[i] != null)
                {
                    Users[i].PC_ShowType = ShowType.Offline;
                    Users[i].APP_ShowType = ShowType.Offline;
                }
            Cache.CloseNoSQL();//关闭本地缓存数据库  
        }
        #endregion 
    }
}
